package com.atguigu.gmall.gateway.filter;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.atguigu.gmall.common.result.Result;
import com.atguigu.gmall.common.result.ResultCodeEnum;
import com.atguigu.gmall.common.util.IpUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpCookie;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.net.URI;
import java.util.List;

/**
 * @Author atguigu-mqx
 * @Description TODO
 * @Date 2023/8/30 10:23
 * @Version 1.0
 */
@Component
public class AuthFilter implements GlobalFilter {

    /*
    authUrls:
        url: trade.html,myOrder.html,list.html--测试
     */
    @Value("${authUrls.url}")
    private String authUrls; // authUrls=trade.html,myOrder.html,list.html

    private AntPathMatcher antPathMatcher = new AntPathMatcher();

    @Autowired
    private RedisTemplate redisTemplate;
    /**
     *
     * @param exchange web请求服务
     * @param chain 过滤器链
     * @return
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //  1.  在网关中限制用户通过浏览器直接访问内部数据接口！
        //  1.1 获取用户访问的地址！
        //  获取请求对象
        ServerHttpRequest request = exchange.getRequest();
        //  获取响应对象
        ServerHttpResponse response = exchange.getResponse();
        //  URI uri = request.getURI(); // http://localhost:80/api/product/inner/getSkuInfo/26
        String path = request.getURI().getPath(); // /api/product/inner/getSkuInfo/26

        //  静态资源过滤：image,css,js ...
        if (antPathMatcher.match("/**/css/**",path) || antPathMatcher.match("/**/js/**",path)
            || antPathMatcher.match("/**/image/**",path)){
            //  放行
            chain.filter(exchange);
        }


        //  /**/inner/**
        if (antPathMatcher.match("/**/inner/**",path)){
            //  应该做出响应
            return this.out(response, ResultCodeEnum.PERMISSION);
        }

        //  2. 获取当前用户Id
        String userId = this.getUserId(request);
        //  获取临时用户Id
        String userTempId = this.getUserTempId(request);
        if ("-1".equals(userId)){
            //  做出响应提示
            return this.out(response, ResultCodeEnum.LOGIN_AUTH);
        }
        //  2.1 限制在未登录的情况下访问 带有/**/auth/** 这样的路径！
        if (antPathMatcher.match("/**/auth/**",path)){
            //  判断用户Id
            if (StringUtils.isEmpty(userId)){
                //  做出响应提示
                return this.out(response, ResultCodeEnum.LOGIN_AUTH);
            }
        }
        //  限制在未登录的情况下访问 哪些业务需要登录，进而跳转到登录页面！
        //  判断 path 中是否包含有需要登录的业务控制器  trade.html myOrder.html list.html
        //  http://list.gmall.com/list.html?category3Id=61
        String[] split = authUrls.split(",");
        if (null != split && split.length>0){
            for (String url : split) {
                if (path.indexOf(url)!=-1 && StringUtils.isEmpty(userId)){
                    //  需要跳转到登录页面.
                    //  设置重定向参数
                    response.setStatusCode(HttpStatus.SEE_OTHER);
                    //  需要记录当前用户访问的地址： request.getURI();
                    response.getHeaders().set(HttpHeaders.LOCATION,"http://passport.gmall.com/login.html?originUrl="+request.getURI());
                    //  重定向 到登录页面！
                    return response.setComplete();
                }
            }
        }

        //  4. 判断当前用户Id 如果不为空，则将用户Id 写入header中
        if (!StringUtils.isEmpty(userId) || !StringUtils.isEmpty(userTempId)){
            if (!StringUtils.isEmpty(userId)) {
                //  将userId,写入header ServerHttpRequest--> 赋值userId
                request.mutate().header("userId",userId).build();
            }
            if (!StringUtils.isEmpty(userTempId)) {
                //  将userTempId,写入header ServerHttpRequest--> 赋值userTempId
                request.mutate().header("userTempId",userTempId).build();
            }
            //  request 对象 转换为ServerWebExchange对象。
            return chain.filter(exchange.mutate().request(request).build());
        }
        //  需要登录的业务被我们记录在 网关中。
        return chain.filter(exchange);
    }

    private String getUserTempId(ServerHttpRequest request){
        //  声明一个变量
        String userTempId = "";
        //  从 cookie 中获取临时用户Id：
        HttpCookie httpCookie = request.getCookies().getFirst("userTempId");
        if (null != httpCookie){
            //  从cookie中获取数据并赋值
            userTempId = httpCookie.getValue();
        } else {
            //    也有可能存储在请求头中
            userTempId = request.getHeaders().getFirst("userTempId");
        }
        //  获取到临时用户Id
        return userTempId;
    }

    /**
     * 获取用户Id
     * @param request
     * @return
     */
    private String getUserId(ServerHttpRequest request) {
        //  用户Id 在缓存.
        String token = "";

        //  获取到token 数据 可能存在header,cookie.
        HttpCookie httpCookie = request.getCookies().getFirst("token");
        if (null != httpCookie){
            //  从cookie 中获取到数据
            token = httpCookie.getValue();
        } else {
            //  从 header 中获取数据
            List<String> list = request.getHeaders().get("token");
            if (!CollectionUtils.isEmpty(list)){
                token = list.get(0);
            }
        }
        //  判断token 是否为空
        if (!StringUtils.isEmpty(token)){
            //  组成缓存的key
            String loginKey = "user:login:"+token;
            //  从缓存获取数据
            String userJson = (String) this.redisTemplate.opsForValue().get(loginKey);
            //  判断当前字符串不为空
            if (!StringUtils.isEmpty(userJson)){
                //  将userJson 转换为JSONObject对象
                JSONObject userObject = JSONObject.parseObject(userJson);
                if (null != userObject){
                    //  获取Ip地址
                    String ip = (String) userObject.get("ip");
                    if (ip.equals(IpUtil.getGatwayIpAddress(request))){
                        //  获取到userId
                        String userId = (String) userObject.get("userId");
                        return userId;
                    }else {
                        //  非法登录。
                        return "-1";
                    }
                }
            }
        }
        //  返回数据
        return "";
    }

    /**
     * 输出数据给信息提示
     * @param response
     * @param resultCodeEnum
     * @return
     */
    private Mono<Void> out(ServerHttpResponse response, ResultCodeEnum resultCodeEnum) {
        //  获取到输出数据
        //  String message = resultCodeEnum.getMessage();
        Result<Object> result = Result.build(null, resultCodeEnum);
        //  设置数据流
        DataBuffer wrap = response.bufferFactory().wrap(JSON.toJSONString(result).getBytes());
        //  页面有个属性设置 Content-Type  text/html  application/json;charset=utf-8;
        response.getHeaders().add("Content-Type","application/json;charset=utf-8");
        //  输出数据
        return response.writeWith(Mono.just(wrap));
    }
}
